package com.universal.common.utils;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.SimpleTimeZone;
import java.util.TimeZone;

/**
 * 日期工具类, 继承org.apache.commons.lang.time.DateUtils类
 *
 * @author austin
 * @data 2022/11/14 10:11
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {

    public static final String FORMAT = "yyyy-MM-dd HH:mm:ss";

    private static final String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"
    };

    /**
     * 得到当前日期字符串 格式（yyyy-MM-dd）
     */
    public static String getDate() {
        return getDate("yyyy-MM-dd");
    }

    /**
     * 得到当前日期字符串 格式（yyyy-MM-dd） pattern可以为："yyyy-MM-dd" "HH:mm:ss" "E"
     */
    public static String getDate(String pattern) {
        return DateFormatUtils.format(new Date(), pattern);
    }

    /**
     * 得到日期字符串 默认格式（yyyy-MM-dd） pattern可以为："yyyy-MM-dd" "HH:mm:ss" "E"
     */
    public static String formatDate(Date date, Object... pattern) {
        String formatDate = null;
        if (date == null) {
            return formatDate;
        }
        if (pattern != null && pattern.length > 0) {
            formatDate = DateFormatUtils.format(date, pattern[0].toString());
        } else {
            formatDate = DateFormatUtils.format(date, "yyyy-MM-dd");
        }
        return formatDate;
    }

    /**
     * 得到日期时间字符串，转换格式（yyyy-MM-dd HH:mm:ss）
     */
    public static String formatDateTime(Date date) {
        return formatDate(date, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 得到当前时间字符串 格式（HH:mm:ss）
     */
    public static String getTime() {
        return formatDate(new Date(), "HH:mm:ss");
    }

    /**
     * 得到当前日期和时间字符串 格式（yyyy-MM-dd HH:mm:ss）
     */
    public static String getDateTime() {
        return formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 得到当前年份字符串 格式（yyyy）
     */
    public static String getYear() {
        return formatDate(new Date(), "yyyy");
    }

    /**
     * 得到当前月份字符串 格式（MM）
     */
    public static String getMonth() {
        return formatDate(new Date(), "MM");
    }

    /**
     * 得到当天字符串 格式（dd）
     */
    public static String getDay() {
        return formatDate(new Date(), "dd");
    }

    /**
     * 得到当前星期字符串 格式（E）星期几
     */
    public static String getWeek() {
        return formatDate(new Date(), "E");
    }

    /**
     * 日期型字符串转化为日期 格式
     * { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm",
     * "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm",
     * "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm" }
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 日期型字符串转化为日期 格式
     * { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm",
     * "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm",
     * "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm" }
     */
    public static Date parseDate(Object str, Date defaultDate) {
        if (str == null) {
            return defaultDate;
        }
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return defaultDate;
        }
    }

    /**
     * 获取过去的天数
     *
     * @param date
     * @return long
     */
    public static long pastDays(Date date) {
        long t = System.currentTimeMillis() - date.getTime();
        return t / (24 * 60 * 60 * 1000);
    }

    /**
     * 获取过去的小时
     *
     * @param date
     * @return long
     */
    public static long pastHour(Date date) {
        long t = System.currentTimeMillis() - date.getTime();
        return t / (60 * 60 * 1000);
    }

    /**
     * 获取过去的分钟
     *
     * @param date
     * @return long
     */
    public static long pastMinutes(Date date) {
        long t = System.currentTimeMillis() - date.getTime();
        return t / (60 * 1000);
    }

    /**
     * 转换为时间（天,时:分:秒.毫秒）
     *
     * @param timeMillis
     * @return string
     */
    public static String formatDateTime(long timeMillis) {
        long day = timeMillis / (24 * 60 * 60 * 1000);
        long hour = (timeMillis / (60 * 60 * 1000) - day * 24);
        long min = ((timeMillis / (60 * 1000)) - day * 24 * 60 - hour * 60);
        long s = (timeMillis / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
        long sss = (timeMillis - day * 24 * 60 * 60 * 1000 - hour * 60 * 60 * 1000 - min * 60 * 1000 - s * 1000);
        return (day > 0 ? day + "," : "") + hour + ":" + min + ":" + s + "." + sss;
    }

    /**
     * 获取两个日期之间的天数
     *
     * @param before
     * @param after
     * @return double
     */
    public static double getDistanceOfTwoDate(Date before, Date after) {
        long beforeTime = before.getTime();
        long afterTime = after.getTime();
        return (afterTime - beforeTime) / (1000 * 60 * 60 * 24);
    }

    /**
     * 獲取時區
     * timeZoneStr格式：UTC+08:00
     */
    public static float getTimeZone(String timeZoneStr) {

        float timeZone = 1f;
        timeZoneStr = timeZoneStr.substring(3);
        if (StringUtils.isBlank(timeZoneStr)) {
            timeZoneStr = "0";
        } else {
            if ("30".equals(timeZoneStr.split(":")[1])) {
                timeZoneStr = timeZoneStr.replace("30", "50");
            }
            timeZoneStr = timeZoneStr.replace(":", ".");
        }
        return Float.valueOf(timeZoneStr);
    }


    /**
     * 根據時區獲取當地時間
     */
    public static String getTimeByZone(float timeZoneOffset) {

        if (timeZoneOffset > 13 || timeZoneOffset < -12) {
            timeZoneOffset = 0;
        }

        int newTime = (int) (timeZoneOffset * 60 * 60 * 1000);
        TimeZone timeZone;
        String[] ids = TimeZone.getAvailableIDs(newTime);
        if (ids.length == 0) {
            timeZone = TimeZone.getDefault();
        } else {
            timeZone = new SimpleTimeZone(newTime, ids[0]);
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(timeZone);

        return sdf.format(new Date());
    }

    /**
     * 日期+-天數
     */
    public static String addDays(int day, String time) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = null;
        try {
            date = sdf.parse(time);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) + day);  //让日期+3
            return sdf.format(calendar.getTime());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 获取日期之间的天数
     *
     * @param date1
     * @param date2
     * @return int
     */
    public static int getDaysBetween(Date date1, Date date2) {
        Calendar d1 = Calendar.getInstance();
        Calendar d2 = Calendar.getInstance();
        d1.setTime(date1);
        d2.setTime(date2);
        if (d1.after(d2)) { // swap dates so that d1 is start and d2 is end
            Calendar swap = d1;
            d1 = d2;
            d2 = swap;
        }
        int days = d2.get(Calendar.DAY_OF_YEAR)
                - d1.get(Calendar.DAY_OF_YEAR);
        int y2 = d2.get(Calendar.YEAR);
        if (d1.get(Calendar.YEAR) != y2) {
            d1 = (Calendar) d1.clone();
            do {
                days += d1.getActualMaximum(Calendar.DAY_OF_YEAR);
                d1.add(Calendar.YEAR, 1);
            } while (d1.get(Calendar.YEAR) != y2);
        }
        return days;
    }


    /**
     * 获取中文日期
     *
     * @param date
     * @return string
     */
    public static String getChineseWeek(Calendar date) {
        final String dayNames[] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        int dayOfWeek = date.get(Calendar.DAY_OF_WEEK);
        return dayNames[dayOfWeek - 1];
    }


    /**
     * 判断某一时间是否在一个区间内
     *
     * @param sourceTime 时间区间,半闭合,如[10:00-20:00)
     * @param curTime    需要判断的时间 如10:00
     * @return boolean
     * @throws IllegalArgumentException
     */
    public static boolean isInTime(String sourceTime, String curTime) {
        if (sourceTime == null || !sourceTime.contains("-") || !sourceTime.contains(":")) {
            throw new IllegalArgumentException("Illegal Argument arg:" + sourceTime);
        }
        if (curTime == null || !curTime.contains(":")) {
            throw new IllegalArgumentException("Illegal Argument arg:" + curTime);
        }
        String[] args = sourceTime.split("-");
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
        try {
            long now = sdf.parse(curTime).getTime();
            long start = sdf.parse(args[0]).getTime();
            long end = sdf.parse(args[1]).getTime();
            if (args[1].equals("00:00")) {
                args[1] = "24:00";
            }
            if (end < start) {
                return now < end || now >= start;
            } else {
                return now >= start && now < end;
            }
        } catch (ParseException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("Illegal Argument arg:" + sourceTime);
        }

    }


    public static Date changeTimeZone(Date date, String oldZone, String newZone) {
        Date dateTmp = null;
        if (date != null) {
            int timeOffset = TimeZone.getTimeZone(oldZone).getRawOffset() - TimeZone.getTimeZone(newZone).getRawOffset();
            dateTmp = new Date(date.getTime() - (long) timeOffset);
        }

        return dateTmp;
    }

    /**
     * 获取某天的开始时间 00：00：00
     */
    public static Date getDayStartTime(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.set(Calendar.HOUR_OF_DAY, 0);
        instance.set(Calendar.MINUTE, 0);
        instance.set(Calendar.SECOND, 0);
        return instance.getTime();
    }

    /**
     * 获取某天的结束时间 23:59:59
     */
    public static Date getDayEndTime(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.set(Calendar.HOUR_OF_DAY, 23);
        instance.set(Calendar.MINUTE, 59);
        instance.set(Calendar.SECOND, 59);
        return instance.getTime();
    }


    /**
     * 根据时间转换成线程安全的日期类
     *
     * @param date
     * @return
     */
    public static LocalDateTime getLocalDateTime(Date date) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        return localDateTime;
    }

    /**
     * 根据线程安全的日期转换位时间
     *
     * @param localDate
     * @return
     */
    public static Date getLocalDateTime(LocalDateTime localDate) {
        Date date = Date.from(localDate.atZone(ZoneId.systemDefault()).toInstant());
        return date;
    }

    /**
     * 将时间字符串转换为对应时区的ZonedDateTime(默认传进来的时间格式是yyyy-MM-dd HH:mm)
     *
     * @param time     ex:yyyy-MM-dd HH:mm
     * @param timezone ex:UTC+8
     * @return
     */
    public static ZonedDateTime strToZoneDateTime(String time, String timezone, String... datePattern) {
        String formatPattern = "yyyy-MM-dd HH:mm";
        if (datePattern != null && datePattern.length > 0) {
            formatPattern = datePattern[0];
        }
        String offset = getOffSet(timezone);
        ZoneId zoneId = ZoneId.ofOffset("UTC", StringUtils.isNotBlank(offset) ? ZoneOffset.of(offset) : ZoneOffset.UTC);
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(formatPattern).withZone(zoneId);
        ZonedDateTime zonedDateTime = ZonedDateTime.parse(time, dateTimeFormatter);
        return zonedDateTime;
    }

    /**
     * 将时间字符串转换为对应ZoneId的ZonedDateTime(默认传进来的时间格式是yyyy-MM-dd HH:mm)
     *
     * @param time   ex:yyyy-MM-dd HH:mm
     * @param zoneId
     * @return
     */
    public static ZonedDateTime strToZoneDateTime(String time, ZoneId zoneId, String... datePattern) {
        String formatPattern = "yyyy-MM-dd HH:mm";
        if (datePattern != null && datePattern.length > 0) {
            formatPattern = datePattern[0];
        }
        if (zoneId == null) {
            zoneId = ZoneId.systemDefault();
        }
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(formatPattern).withZone(zoneId);
        ZonedDateTime zonedDateTime = ZonedDateTime.parse(time, dateTimeFormatter);
        return zonedDateTime;
    }

    /**
     * 字符串转LocalDateTime
     *
     * @param time
     * @param datePattern
     * @return
     */
    public static LocalDateTime strToLocalDateTime(String time, String datePattern) {
        return LocalDateTime.parse(time, DateTimeFormatter.ofPattern(datePattern));
    }

    /**
     * 获取时区偏移量
     *
     * @param timezone
     * @return
     */
    public static String getOffSet(String timezone) {
        return timezone.replace("UTC", "");
    }

    /**
     * 根据时区获取对应的当前时间
     *
     * @param timezone
     * @return
     */
    public static ZonedDateTime getZonedDateTimeByTimezone(String timezone) {
        String offset = DateUtils.getOffSet(timezone);
        return ZonedDateTime.now().withZoneSameInstant(ZoneId.ofOffset("UTC", StringUtils.isNotBlank(offset) ? ZoneOffset.of(offset) : ZoneOffset.UTC));
    }


    //joda-time

    //str->Date
    //Date->str
    public static Date strToDate(String dateTimeStr, String formatStr) {
        org.joda.time.format.DateTimeFormatter dateTimeFormatter = org.joda.time.format.DateTimeFormat.forPattern(formatStr);
        DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
        return dateTime.toDate();
    }

    public static String dateToStr(Date date, String formatStr) {
        if (date == null) {
            return StringUtils.EMPTY;
        }
        DateTime dateTime = new DateTime(date);
        return dateTime.toString(formatStr);
    }

    public static Date strToDate(String dateTimeStr) {
        org.joda.time.format.DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(FORMAT);
        DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
        return dateTime.toDate();
    }

    public static String dateToStr(Date date) {
        if (date == null) {
            return StringUtils.EMPTY;
        }
        DateTime dateTime = new DateTime(date);
        return dateTime.toString(FORMAT);
    }


    public static void main(String[] args) {
        System.out.println(DateUtils.dateToStr(new Date(), "yyyy-MM-dd HH:mm:ss"));
        System.out.println(DateUtils.strToDate("2023-01-01 11:11:11", "yyyy-MM-dd HH:mm:ss"));
    }
}
